home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Apple Macintosh Developer Technical Support
- ** Simple Tear-Off Menu Definition
- **
- ** File: TearOff.MDEF.c
- ** Written by: C.K. Haun and Eric Soldan
- **
- ** Copyright © 1992 Apple Computer, Inc.
- ** All rights reserved.
- */
-
-
- /*
- What does it take to tear a menu off? Do you need to patch things?
- Do you need to write an assembly language interface to the Menu Manager?
- Do I need weird stand-alone globals?
-
- Nah, just a little MDEF.
-
- And here it is. This MDEF 'tears off', allowing you to know the user
- has torn it off, at which point you'll make a palette window for it.
- No patches, no hacking, just code.
-
- This MDEF is also an Iconic MDEF, but the same technique will work
- with a Text menu, or anything else.
-
- How The Tear Off Part Works:
- When the user is tracking around in a menu, you are periodically called to
- highlight and de-highlight the items the user is tracking over.
- This is the whole key to tearing off.
- When the user goes outside the bounding rectangle of the menu, the
- Menu Manager tells you to de-highlight the last item they were over, and
- does NOT tell you to highlight a new one.
- This MDEF watches for that situation, when it sees that there are no items
- highlighted (easier than you'd think) it checks to see if the user is
- dragging in the Desktop area (screen minus this menu and the menu bar) and
- if they are, it drags a gray outline of the menu around until they
- 1) release the mouse.
- 2) go back in the menu or menu bar.
-
- If they release the mouse in a valid area for a tear off (not in the menu bar or current menu)
- then the MDEF lets you, the application, know that the user tore off.
- It does this by passing back an item number of -1, that's the
- flag to you that says "Hey, something tore it".
- The 'where' the user wants is a little sneakier.
- The Menu struture that this MDEF uses has a dummy item at the end.
- That dummy item is never drawn, it's just a placeholder.
- When the user tears-off, I stuff the rectangle that they tore off at
- in the last menu item, as bytes 1 through 8 in the string.
- So in your application, you can do a
- GetItem(theMenu,CountMItems(theMenu),tempString);
- BlockMove((Ptr)tempString + 1), (Ptr)&myTornOffRect, sizeof(Rect));
- and create your palette at that rect.
-
- Why not something like:
- GetItem(theMenu,CountMItems(theMenu),tempString);
- myTornOffRect = *(Rect *)(tempString + 1);
- Because this code will crash on 68000 machines (a move with an odd address, ya know).
- So do the ugly BlockMove and you'll be happy.
-
- Pretty simple, huh?
-
-
- Pay the most attention to the code in the ChooseIt and DragTearOff functions.
-
- How The Icon Part Works:
- Drawing icons instead of text in a menu is not hard, the only thing that's
- at all interesting in that part of the code is how I store the IDs of the
- menu icons.
-
- I do NOT store the actual numeric values of the icon ids in the menu structure.
- You could, but then you have to actually *think* when you are building your
- menu in ResEdit or whereever, and I like to avoid extra thought when I can.
- So, I store the icon resource IDs in their string representation, so I do a
- StringToNum(...) to get the real number to pass to GetIcon.
- This has been extended to allow alternate cursors per menu item. By marking the menu item,
- you can determine which icon to actually draw. DTS.Draw uses this facility, so there is a
- complete sample demonstrating this.
-
- The special dummy menu item on the end does more than just return the rect in bytes 1-8.
- It also allows you to use ResEdit to determine other attributes of the menu without
- having to modify and recompile the MDEF. You can have 2 through 8 extra numbers after
- the length byte and 8 reserved bytes for the rect. They are:
-
- 1) Item height
- 2) Item width
- 3) Icon height
- 4) Icon width
- 5) Icon height offset
- 6) Icon width offset
- 7) Icon border height
- 8) Icon border width
-
- All fields that are set to a value in ResEdit are assumed to be 0, except for the
- Icon height and Icon width fields. These are assumed to be the same as the Item height
- and Item width unless otherwise stated.
- (You must have at least the Item height and Item width).
-
-
- • Things that AREN'T in here! •
- This MDEF does NOT scroll, support heirarchical menus, or PopUps.
- The thought of a Hierarchical tear-off makes my brain throb.
-
- If you need more information about HMenus, scrolling, and Popups, please
- see the excellent MDEF sample called Concordia on the Developer CD.
-
- Any bugs or comments....
- C.K. Haun or Eric Soldan
- Apple DTS (ALink : DEVSUPPORT)
-
-
- */
-
- #include <Types.h>
- #include <Quickdraw.h>
- #include <Controls.h>
- #include <Events.h>
- #include <Memory.h>
- #include <ToolUtils.h>
- #include <Menus.h>
- #include <Packages.h>
- #include <Resources.h>
- #include <Script.h>
- #include <GestaltEqu.h>
-
-
- #define kSlop 6
- #define kWDEFTitleSize 9
- #define kExtremeNeg -32767 + 1
- #define kExtremePos 32767 - 1
-
- #define kItemRect 0
- #define kIconRect 1
- #define kMaxNumParms 8
-
- #define kItemHeight 0
- #define kItemWidth 1
- #define kIconHeight 2
- #define kIconWidth 3
- #define kIconHeightOffset 4
- #define kIconWidthOffset 5
- #define kIconBorderHeight 6
- #define kIconBorderWidth 7
-
- #define kQDOriginal 0
-
- pascal void main(short message, MenuHandle menu, Rect *menuRect, Point hit, short *item);
-
- void DrawItem (MenuHandle menu, Rect menuRect, short item);
- void ChooseIt (MenuHandle menu, Rect menuRect, Point hit, short *item);
- Rect CalcItemRect (MenuHandle menu, Rect menuRect, short item, Boolean rectType);
- RgnHandle CalcItemSelectRgn(MenuHandle menu, Rect menuRect, short item);
- short GetNumItems (MenuHandle menu);
- short GetMenuData (MenuHandle menu, short data[]);
- Boolean DragTearOff (Rect menuRect, Point thePoint, Rect *passedRect);
- Rect FrameGrayRect (Rect rct);
-
- Rect GetMainScreenRect(void);
- long GetGestaltResult(OSType gestaltSelector);
-
-
-
- /*****************************************************************************/
- /*****************************************************************************/
-
-
-
- pascal void main(short message, MenuHandle menu, Rect *menuRect, Point hit, short *item)
- {
- short qq, numItems, data[kMaxNumParms];
-
- switch (message) {
- case mDrawMsg:
- numItems = GetNumItems(menu);
- for (qq = 1; qq <= numItems; qq++) DrawItem(menu, *menuRect, qq);
- break;
-
- case mChooseMsg:
- ChooseIt(menu, *menuRect, hit, item);
- break;
-
- case mSizeMsg:
- numItems = GetMenuData(menu, data);
- (*menu)->menuHeight = data[kItemHeight] * numItems - 1;
- (*menu)->menuWidth = data[kItemWidth];
- break;
-
- case mPopUpMsg: /* I am not supporting Popups, so I'm ignoring these */
- case mDrawItemMsg:
- case mCalcItemMsg:
- break;
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* ChooseIt is the main workhorse routine. */
- /* The Menu Manager calls this routine continually during */
- /* tracking to highlight and dehighlight the items the user is tracking over */
-
- void ChooseIt(MenuHandle menu, Rect menuRect, Point hit, short *item)
- {
- short ii, numItems;
- RgnHandle rgn;
- Str63 infoText;
- Rect itemRect, tearRect;
- Boolean justUnselected;
-
- numItems = GetNumItems(menu);
-
- if (*item == -1) {
- GetItem(menu, numItems + 1, infoText);
- BlockMove(infoText + 1, (Ptr)&tearRect, sizeof(Rect));
- InsetRect(&tearRect, -1, -1);
- FrameGrayRect(tearRect);
- return;
- }
-
- if ((*item < 0) || (*item > numItems)) return;
- /* Make sure values are valid. */
-
- for (ii = numItems; ii; --ii) {
- itemRect = CalcItemRect(menu, menuRect, ii, kItemRect); /* Get rect for first/next item. */
- if (PtInRect(hit, &itemRect)) break; /* Are we in an item? If so, we
- ** know what we want to know. */
- }
-
- justUnselected = false;
-
- if (*item != ii) { /* If things have changed... */
-
- if (*item) { /* If we had an old selected item, deselect it. */
- rgn = CalcItemSelectRgn(menu, menuRect, *item);
- InvertRgn(rgn);
- DisposeRgn(rgn);
- justUnselected = true;
- }
-
- if (*item = ii) { /* If we have a new selected item, select it. */
- rgn = CalcItemSelectRgn(menu, menuRect, ii);
- InvertRgn(rgn);
- DisposeRgn(rgn);
- }
- }
-
- if (justUnselected) { /* If we just unselected an item... */
-
- if (!ii) { /* If we haven't selected a new item... */
-
- if (DragTearOff(menuRect, hit, &tearRect)) {
-
- /* if this returned true, then they let up on the mouse whilst they were dragging */
- /* the outline, otherwise they went back and we press on */
- /* So, the final rectangle is in passedRect. */
- /* And, as I mentioned above, we have a dummy item at the end of our menu. */
- /* That dummy item is where we will sneakily store the rectangle, like so... */
-
- GetItem(menu, ++numItems, infoText);
- BlockMove((Ptr)&tearRect, infoText + 1, sizeof(Rect));
- SetItem(menu, numItems, infoText);
-
- *item = -1;
- }
- }
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* DrawItem draws our item.
- /* In this MDEF, I grab the text of the menu item, change it */
- /* to a number, and then get an icon with that resID */
- /* and plot that icon */
-
- void DrawItem(MenuHandle menu, Rect menuRect, short theItem)
- {
- Rect itemRect, iconRect;
- Str32 itemText, str;
- long num;
- short mark, i, ii;
- Handle itemIcon;
- RgnHandle oldClip, newClip;
-
- GetItemMark(menu, theItem, &mark);
- if (mark >= '0') mark -= '0';
-
- GetItem(menu, theItem, itemText);
- itemText[++itemText[0]] = ','; /* Give us a terminator character. */
- for (ii = 0; mark > -1; --mark) {
- if (ii >= itemText[0]) break;
- for (i = 0; itemText[++ii] != ','; ++i);
- BlockMove(itemText + ii - i, str + 1, str[0] = i);
- StringToNum(str, &num);
- }
-
- if (itemIcon = GetResource('ICN#', num)) {
- iconRect = CalcItemRect(menu, menuRect, theItem, kIconRect);
- SectRect(&iconRect, &menuRect, &iconRect);
- oldClip = NewRgn();
- newClip = NewRgn();
- RectRgn(newClip, &iconRect);
- GetClip(oldClip);
- SetClip(newClip);
- iconRect.right = iconRect.left + 32;
- iconRect.bottom = iconRect.top + 32;
- PlotIcon(&iconRect, itemIcon);
- SetClip(oldClip);
- DisposeRgn(oldClip);
- DisposeRgn(newClip);
- ReleaseResource(itemIcon);
- itemRect = CalcItemRect(menu, menuRect, theItem, kItemRect);
- itemRect.top = itemRect.bottom - 1;
- FrameRect(&itemRect);
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* DragTearOff */
- /* DragTearOff does the actual tearing off */
- /* As long as the mouse stays outside the menu and the menu bar area, I will */
- /* drag a rectangle around. */
- /* This ends if */
- /* 1) The user lets up on the mouse. In that case, I return the ending rectangle */
- /* and a TRUE */
- /* 2) The user goes back into the menu or menu bar. In that case, I return FALSE */
-
- Boolean DragTearOff(Rect menuRect, Point thePoint, Rect *passedRect)
- {
- Rect dragRect, menubarRect;
- Point endPoint, pp;
- long ll;
- Boolean dragRectOn;
-
- if (PtInRect(thePoint, &menuRect)) return(false);
- /* If currently in the menu, get out of here. */
-
- menubarRect = GetMainScreenRect(); /* Calc the menubar rect. */
- menubarRect.bottom = menubarRect.top + GetMBarHeight();
-
- if (PtInRect(thePoint, &menubarRect)) return(false);
- /* If currently in the menubar, get out of here. */
-
- dragRect = menuRect; /* Drag this puppy around until the user lets go,
- ** or until the user bumps into the badRgn. */
-
- InsetRect(&dragRect, kSlop, kSlop);
- ll = PinRect(&dragRect, thePoint);
- InsetRect(&dragRect, -kSlop, -kSlop);
-
- pp = *(Point *)≪
- OffsetRect(&dragRect, thePoint.h - pp.h, thePoint.v - pp.v);
- /* Get the rect to be over the mouse location by at least a little slop. */
-
- InsetRect(&dragRect, -1, -1);
-
- /* Keep tracking the mouse and moving the rect until they release the mouse button,
- ** or until they bump into the menu or the menubar. */
-
- dragRectOn = false;
-
- while (StillDown()) {
-
- GetMouse(&endPoint);
-
- if (!EqualPt(endPoint, thePoint)) { /* They moved... */
-
- if (dragRectOn) {
- FrameGrayRect(dragRect); /* Erase the old rect */
- dragRectOn = false;
- }
-
- if (PtInRect(thePoint, &menuRect)) break;
- if (PtInRect(thePoint, &menubarRect)) break;
-
- OffsetRect(&dragRect, endPoint.h - thePoint.h, endPoint.v - thePoint.v);
- thePoint = endPoint;
-
- InsetRect(&menuRect, -kSlop, -kSlop);
- dragRectOn = !PtInRect(thePoint, &menuRect);
- InsetRect(&menuRect, kSlop, kSlop);
-
- if (dragRectOn) FrameGrayRect(dragRect); /* Frame the new position */
- Delay(1, &ll); /* Reduce tearing. */
- }
- }
-
- if (dragRectOn) dragRect = FrameGrayRect(dragRect); /* Erase the gray region. */
- InsetRect(&dragRect, 1, 1);
-
- *passedRect = dragRect; /* pass back the end rect position, whatever it is */
- return(dragRectOn); /* and go hither */
- }
-
-
-
- /*****************************************************************************/
-
-
-
- Rect CalcItemRect(MenuHandle menu, Rect menuRect, short item, Boolean rectType)
- {
- Rect itemRect;
- short data[kMaxNumParms];
-
- GetMenuData(menu, data);
-
- itemRect.top = --item * data[kItemHeight];
- switch (rectType) {
- case kItemRect:
- SetRect(&itemRect, 0, itemRect.top, data[kItemWidth], itemRect.top + data[kItemHeight]);
- break;
- case kIconRect:
- SetRect(&itemRect, 0, itemRect.top, data[kIconWidth], itemRect.top + data[kIconHeight]);
- OffsetRect(&itemRect, data[kIconWidthOffset], data[kIconHeightOffset]);
- break;
- }
-
- OffsetRect(&itemRect, menuRect.left, menuRect.top);
- return(itemRect);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- RgnHandle CalcItemSelectRgn(MenuHandle menu, Rect menuRect, short item)
- {
- Rect rct;
- RgnHandle rgn, interiorRgn;
- short data[kMaxNumParms];
-
- rct = CalcItemRect(menu, menuRect, item, kItemRect);
- --rct.bottom;
- RectRgn(rgn = NewRgn(), &rct);
-
- GetMenuData(menu, data);
- if (data[kIconBorderHeight] || data[kIconBorderWidth]) {
- InsetRect(&rct, data[kIconBorderWidth], data[kIconBorderHeight]);
- RectRgn(interiorRgn = NewRgn(), &rct);
- DiffRgn(rgn, interiorRgn, rgn);
- DisposeRgn(interiorRgn);
- }
-
- return(rgn);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- short GetMenuData(MenuHandle menu, short data[])
- {
- short numItems, ii, parm, i;
- Str63 infoText;
- Str15 str;
- long num;
-
- for (i = 0; i < kMaxNumParms; ++i) data[i] = 0;
-
- GetItem(menu, (numItems = GetNumItems(menu)) + 1, infoText);
- infoText[++infoText[0]] = ','; /* Give us a terminator character. */
-
- ii = 8;
- if (infoText[9] == ',') ++ii; /* Skip possible first optional delimiter. */
-
- for (parm = 0; parm < kMaxNumParms; ++parm) {
- if (ii >= infoText[0]) break;
- for (i = 0; infoText[++ii] != ','; ++i);
- BlockMove(infoText + ii - i, str + 1, str[0] = i);
- StringToNum(str, &num);
- data[parm] = num;
- if (parm < 2) data[parm + 2] = num;
- }
-
- return(numItems);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- short GetNumItems(MenuHandle menu)
- {
- return(CountMItems(menu) - 1);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- Rect FrameGrayRect(Rect frameRect)
- {
- PenState thePen;
- RgnHandle oldClip;
- Pattern Gray;
- Rect rct;
- short mbh;
-
- GetPenState(&thePen); /* I'm going to be changing pen information, so save the old */
-
- /* get the pattern I want from the System file, since I don't have */
- /* access to Application Globals (A5) since I'm standalone */
- /* I _could_ rely on the current port information, but I'm too */
- /* paranoid for that */
-
- Gray[0] = 0;
- GetIndPattern(Gray, sysPatListID, 4);
-
- GetClip(oldClip = NewRgn());
- SetRect(&rct, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
- ClipRect(&rct); /* Allow drawing everywhere. */
-
- PenMode(patXor); /* set up for gray dragging */
- PenPat(&Gray);
-
- mbh = GetMBarHeight() + kWDEFTitleSize;
- if (frameRect.top < mbh) OffsetRect(&frameRect, 0, (mbh - frameRect.top));
- FrameRect(&frameRect);
-
- SetPenState(&thePen); /* restore the pen */
- SetClip(oldClip); /* restore the original clip */
- DisposeRgn(oldClip);
-
- return(frameRect);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- Rect GetMainScreenRect(void)
- {
- short qdVers;
- GDHandle mainDevice;
- GrafPtr mainPort;
-
- qdVers = (GetGestaltResult(gestaltQuickdrawVersion) >> 8) & 0xFF;
-
- if (qdVers > kQDOriginal) {
- mainDevice = GetMainDevice();
- return((*mainDevice)->gdRect);
- }
- else {
- GetWMgrPort(&mainPort);
- return(mainPort->portRect);
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- long GetGestaltResult(OSType gestaltSelector)
- {
- long gestaltResult;
-
- if (Gestalt(gestaltSelector, &gestaltResult) == noErr)
- return(gestaltResult);
- else
- return(0);
- }
-
-
-
-